package com.ferry.taskflow.taskFlow.tx;

import com.ferry.taskflow.taskFlow.base.TaskExecutor;
import com.ferry.taskflow.taskFlow.base.enumv.TaskState;
import com.ferry.taskflow.taskFlow.domain.TaskResult;

/**
 * Created by daiyong
 * 事务任务必须继承此task
 */
@Slf4j
public class TxTaskExecutor<RESULT> extends TaskExecutor<RESULT> {

	private TxStage txStage;

	private TxCoordinater txCoordinater;

	private DataSourceTransactionManager dataSourceTransactionManager;

	public TxTaskExecutor(TxTask<RESULT> txTask) {
		super(txTask);
	}

	@Override
	public void execute() {

		this.dataSourceTransactionManager = txStage.txFlow().dataSourceTransactionManager();
		this.txCoordinater = txStage.txFlow().txCoordinater();

		DefaultTransactionDefinition dtf = new DefaultTransactionDefinition();
		dtf.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
		TransactionStatus status = dataSourceTransactionManager.getTransaction(dtf);//开始事务

		try {

			//修改任务状态
			if (!this.state.compareAndSet(TaskState.INIT.getCode(), TaskState.DOING.getCode())) {
				log.warn("task is running. id:{}");
			}

			RESULT result = task.execute();

			txCoordinater.successReady();

			//回滚事务
            if (txCoordinater.shouldRollback()) {
				dataSourceTransactionManager.rollback(status);

				//回调
				((TxTask)task).onRollback(taskResult, result);

				taskResult = new TaskResult(false, false, null);
			}
			//提交事务
			else {
				dataSourceTransactionManager.commit(status);

				//回调
				task.onSuccess(taskResult, result);

				taskResult = new TaskResult(true, true, null);
			}

		} catch (Throwable throwable) {

			log.error("TxTaskExecutor Exception", throwable);

			//回滚事务
			dataSourceTransactionManager.rollback(status);

			txCoordinater.failReady();

			//回调
			taskResult = new TaskResult(false, false, throwable);

			((TxTask)task).onRollback(taskResult, null);

			//设置状态
			this.state.compareAndSet(TaskState.DOING.getCode(), TaskState.EXCEPTION.getCode());

		}
	}

	public TxTaskExecutor txStage(TxStage txStage) {
		this.txStage = txStage;
		return this;
	}


}
